﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WeChatTool
{
	/// <summary>
	/// 队列
	/// </summary>
	[Serializable]
	public class CustomQueue<T> :
		ICollection<T>, IEnumerable<T>, IEnumerable, IEnumerator, IEnumerator<T>, IReadOnlyCollection<T>
	{
		/// <summary>
		/// 数组的容量
		/// </summary>
		public int Capacity { get; private set; }

		/// <summary>
		/// 数组现在有多少个元素
		/// </summary>
		public int Count { get; private set; }

		public bool IsReadOnly => false;

		T IEnumerator<T>.Current => this.Dequeue();

		object IEnumerator.Current => this.Dequeue();

		/// <summary>
		/// 数组
		/// </summary>
		private T[] array;

		/// <summary>
		/// 第一个元素的位置
		/// </summary>
		private int first;

		/// <summary>
		/// 最后一个元素的位置
		/// </summary>
		private int last;


		public CustomQueue() : this(10)
		{
		}

		/// <summary>
		/// 构造
		/// </summary>
		/// <param name="capacity">初始容量的大小</param>
		public CustomQueue(int capacity)
		{
			array = new T[capacity];
			Capacity = capacity;
		}

		/// <summary>
		/// 构造
		/// </summary>
		/// <param name="ienum">开始给队列添加元素，是一个迭代器</param>
		public CustomQueue(IEnumerable<T> ienum)
			: this(ienum.Count())
		{
			foreach (var item in ienum)
			{
				Enqueue(item);
			}
		}

		/// <summary>
		/// 移除并返回第一位元素
		/// </summary>
		/// <returns></returns>
		public T Dequeue()
		{
			if (Count == 0)
				ThrowError("数组为空，没有办法获得第一个数组");

			//缩容
			if (Count <= Capacity / 4)
			{
				ResetArray(Capacity / 2);
			}

			Count--;
			T temp = array[first];
			array[first] = default(T);
			first = ++first % Capacity;

			return temp;
		}

		/// <summary>
		/// 再尾部添加元素
		/// </summary>
		/// <param name="item">需要添加的元素</param>
		public void Enqueue(T item)
		{
			//扩容
			if (Count == Capacity)
			{
				ResetArray(Capacity * 2);
			}

			Count++;
			array[last] = item;
			last = ++last % Capacity;
		}

		/// <summary>
		/// 看一下第一个元素，不移除
		/// </summary>
		/// <returns></returns>

		public T Peek()
		{
			if (Count == 0)
				ThrowError("数组为空，没有办法获得第一个数组");
			return array[first];
		}

		/// <summary>
		/// 重置数组的大小容量
		/// </summary>
		/// <param name="capacity">新的数组的大小容量</param>
		private void ResetArray(int capacity)
		{
			T[] newArray = new T[capacity];
			if (Count > 0)
			{
				if (first < last)
				{
					Array.Copy(array, first, newArray, 0, Count);
				}
				else
				{
					int temp = this.Capacity - first;
					Array.Copy(array, first, newArray, 0, temp);
					Array.Copy(array, 0, newArray, temp, last);
				}
			}
			first = 0;
			last = Count;
			this.Capacity = capacity;
			array = newArray;
		}

		private void ThrowError(string message)
		{
			throw new ArgumentException(message);
		}

		void ICollection<T>.Add(T item)
		{
			Enqueue(item);
		}

		void ICollection<T>.Clear()
		{
			Array.Clear(array, 0, Capacity);
			first = 0;
			last = 0;
			Count = 0;
		}

		/// <summary>
		/// 是否包含这个元素
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public bool Contains(T item)
		{
			int tempFirst = first;
			for (int i = 0; i < Count; i++)
			{
				if (array[tempFirst].Equals(item))
				{
					return true;
				}
				else
				{
					tempFirst = ++tempFirst & Capacity;
				}
			}
			return false;
		}

		public void CopyTo(T[] array, int arrayIndex)
		{
			if (object.ReferenceEquals(array, null))
				ThrowError("复制到数组为空");
			else if (arrayIndex >= Capacity)
				ThrowError("开始复制的数组索引超出Array的大小");
			else if (arrayIndex < 0)
				ThrowError("开始复制的数组索引不能小于0");

			int tempFirst = first;
			for (int i = 0; i < Count; i++)
			{
				array[i] = this.array[tempFirst];
				tempFirst = ++tempFirst % Capacity;
			}
		}

		public bool Remove(T item)
		{
			ThrowError("队列不支持此方法");
			return false;
		}

		public IEnumerator<T> GetEnumerator()
		{
			return this;
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return this;
		}

		public void Dispose()
		{

		}

		public bool MoveNext()
		{
			return Count > 0;
		}

		public void Reset()
		{
		}

		internal T[] SelectAll(Func<FileChangeInformation, bool> value)
		{
			return array;
		}
	}
}
